package jomodel;


import java.util.*;


/**
 * Describes a triangular face.  Holds references to three vertices,
 * their normals and texture coodinates.  Vertex normals are stored
 * here and not in the Vertex object because a vertex may be shared
 * between two or more faces, and the faces may have very different
 * normals (ie. if the faces are at a 90 degree angle and make a
 * sharp edge).
 *
 * To smooth normals and preserve sharp edges, the Triangle object
 * holds the neighboring triangles for each vert.  Based on the
 * angles between this triangle and its neighbors, the smoothing
 * algorithm can decide whether to smooth the normals across the
 * triangles, or to use the face normal to create a hard edge.
 *
 * jun 2006: added makeClone()
 */
public class JOTriangle {

    public JOVertex p1;  // first  vertex
    public JOVertex p2;  // second vertex
    public JOVertex p3;  // third  vertex

    public JOVector norm1; // normal at vert 1
    public JOVector norm2; // normal at vert 2
    public JOVector norm3; // normal at vert 3

    public JOVector uvw1 = new JOVector();; // texture coord at vert 1
    public JOVector uvw2 = new JOVector();; // texture coord at vert 2
    public JOVector uvw3 = new JOVector();; // texture coord at vert 3

    public ArrayList<JOTriangle> neighborsP1 = new ArrayList<JOTriangle>(); // Neighbor triangles of vertex1
    public ArrayList<JOTriangle> neighborsP2 = new ArrayList<JOTriangle>(); // Neighbor triangles of vertex2
    public ArrayList<JOTriangle> neighborsP3 = new ArrayList<JOTriangle>(); // Neighbor triangles of vertex3

    public JOVector n = new JOVector();  // Normal vector of flat triangle
    public float Zdepth = 0f;               // screen Z depth
    public int ID = 0;
    public int groupID = 0;
    int materialID;  // index into materials array


    public JOTriangle(JOVertex a, JOVertex b, JOVertex c) {
        p1=a;
        p2=b;
        p3=c;
    }

    /**
     * Calculate the face normal for this triangle
     */
    public void recalcFaceNormal() {
        n = JOVector.getNormal(p1.pos,p2.pos,p3.pos);
    }


    /**
     * Recalculate the vertex normal, by averagin the normals
     * of the neighboring triangles.  The neighbor list holds
     * only triangles that we want to average with this vertex.
     *
     * @see GL_Object.registerNeighbors()
     * @param neighbors neighboring triangles for this vert
     * @return  vertex normal
     */
    public JOVector recalcVertexNormal(ArrayList<JOTriangle> neighbors) {
        float nx = 0;
        float ny = 0;
        float nz = 0;
        JOTriangle tri;
        JOVector wn = new JOVector();
        // for each neighbor triangle, average the normals
        for (int i=0; i < neighbors.size(); i++) {
            tri = neighbors.get(i);
            wn = tri.getWeightedNormal();
            nx += wn.x;
            ny += wn.y;
            nz += wn.z;
        }
        JOVector vertn = new JOVector(nx, ny, nz);
        vertn.normalize();
        return vertn;
    }


    public JOVector getWeightedNormal() {
        return JOVector.vectorProduct(p1.pos,p2.pos,p3.pos);
    }


    public JOVector getCenter() {
        float cx=(p1.pos.x+p2.pos.x+p3.pos.x)/3;
        float cy=(p1.pos.y+p2.pos.y+p3.pos.y)/3;
        float cz=(p1.pos.z+p2.pos.z+p3.pos.z)/3;
        return new JOVector(cx,cy,cz);
    }

    public void resetNeighbors() {
        neighborsP1.clear();
        neighborsP2.clear();
        neighborsP3.clear();
    }

    /**
     * Calculate average screen Z depth of this triangle.  This
     * function requires that the vertex screen positions have been
     * set (vertex.posS).
     * @see GL_Mesh.project()
     */
    public void calcZdepth() {
        Zdepth = (p1.posS.z + p2.posS.z + p3.posS.z)/3f;
    }

    /**
     * Return true if two triangles should be smoothed as one
     * surface.  cos_angle is the minumum angle for smoothing.
     * If the angle between the faces is > cos_angle, then the
     * faces are considered to be a continuous surface.  Ie.
     * 90 degrees is a sharp corner, 180 degrees is a flat surface.
     */
    public static boolean onSameSurface(JOTriangle t1, JOTriangle t2, float cos_angle) {
        float dot = JOVector.dotProduct(t1.n, t2.n);
        //System.out.println("surface: compare dot=" +dot + " cos-angle=" + cos_angle + " return " + (dot > cos_angle));
        return (dot > cos_angle);
    }

    // MJN: have to clone the vertices, otherwise the cloned triangle
    // points back to the same verts as the original triangle.
    // WARNING: this means that the triangle verts aren't references to the
    // verts in the vertex array.  The triangle verts are copies, so if
    // changes are made to the verts (ie. rebuild() assigns ids to them),
    // these copies won't be affected.
    public JOTriangle makeClone() {
        JOTriangle clone = new JOTriangle(p1.makeClone(),p2.makeClone(),p3.makeClone());
        clone.norm1 = norm1.getClone();
        clone.norm2 = norm2.getClone();
        clone.norm3 = norm3.getClone();
        clone.uvw1 = uvw1.getClone();
        clone.uvw2 = uvw2.getClone();
        clone.uvw3 = uvw3.getClone();
        clone.neighborsP1 = (ArrayList<JOTriangle>)neighborsP1.clone();
        clone.neighborsP2 = (ArrayList<JOTriangle>)neighborsP2.clone();
        clone.neighborsP3 = (ArrayList<JOTriangle>)neighborsP3.clone();
        return clone;
    }
}